package com.ihr360.payroll.service.salarytasklet.step.processor;

import com.google.common.collect.ImmutableList;
import com.ihr360.alps.api.ServiceProvider;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.model.type.FormulaFrom;
import com.ihr360.commons.model.type.RoundingMode;
import com.ihr360.commons.model.type.SalaryAdjustFormula;
import com.ihr360.expr.ExprException;
import com.ihr360.expr.engine.VariableID;
import com.ihr360.expr.parser.ExprToken;
import com.ihr360.job.core.annotation.BeforeProcess;
import com.ihr360.job.core.item.ItemProcessor;
import com.ihr360.job.core.repository.JobRepository;
import com.ihr360.job.core.scope.context.SimpleStepContext;
import com.ihr360.payroll.model.dto.salary.basecode.SalaryFieldType;
import com.ihr360.payroll.model.dto.salary.embeddable.SalaryFieldValue;
import com.ihr360.payroll.model.dto.salary.embeddable.SystemSalaryFieldValue;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.meta.system.SystemSalaryField;
import com.ihr360.payroll.model.type.SalaryPhase;
import com.ihr360.payroll.service.alps.profile.SalaryAdjustServiceProfile;
import com.ihr360.payroll.service.salarytasklet.expr.engine.SalaryDependencyEngine;
import com.ihr360.payroll.service.salarytasklet.expr.engine.SalaryExprEngineProvider;
import com.ihr360.payroll.service.salarytasklet.model.SimplePayrollConfig;
import com.ihr360.payroll.service.salarytasklet.step.SalaryCalcContext;
import com.ihr360.validation.ValidationUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * @author davidwei
 */
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PayrollConfigProcessor implements ItemProcessor<SimplePayrollConfig, Map<String, SalaryField>> {
    private SimpleStepContext simpleStepContext;

    private JobRepository jobRepository;

    private SalaryCalcContext salaryCalcContext;


    public void setSalaryCalcContext(SalaryCalcContext salaryCalcContext) {
        this.salaryCalcContext = salaryCalcContext;
    }

    @Override
    public Map<String, SalaryField> process(SimplePayrollConfig payrollConfig) throws Exception {
        return processStatisticsFields(payrollConfig);
    }

    private Map<String, SalaryField> processStatisticsFields(SimplePayrollConfig simplePayrollConfig) throws Exception{

        SalaryDependencyEngine engine = salaryCalcContext.dependencyEngineHolder.getWrittenItem();
        if (engine == null) {
            engine = new SalaryDependencyEngine(new SalaryExprEngineProvider(salaryCalcContext.variableFetcher));
            salaryCalcContext.dependencyEngineHolder.write(ImmutableList.of(engine));
        }
        engine.setAutoCalculate(false);

        SalaryAdjustFormula salaryAdjustFormula = simplePayrollConfig.getSalaryAdjustFormula();
        List<SalaryField> salaryAdjustReferenceFields = simplePayrollConfig.getSalaryAdjustReferenceFields();
        if (CollectionUtils.isNotEmpty(salaryAdjustReferenceFields) && !SalaryAdjustFormula.FORMULA0.equals(salaryAdjustFormula)) {
            String formula = salaryAdjustFormula.getFormula();
            try {
                //添加分段基数公式
                //设置分段计薪调整项目
                List<String> codes = salaryAdjustReferenceFields.stream().map(s -> VariableID.buildVariableWithSheetIdAndFieldId(SalaryPhase.PHASE1.toString(), s.getCode())).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(codes)) {
                    String basement = String.join(ExprToken.PLUS.val, codes);
                    String variableId = VariableID.buildVariableId(ServiceProvider.SERVICE_ID.SALARY_ADJUST_RULE, SalaryAdjustServiceProfile.calcBase.getCode());
                    String finalFormula = ExprToken.EQUAL.val.concat(basement);
                    engine.set(variableId,finalFormula );
                }
            } catch (ExprException e) {
                jobRepository.error(simpleStepContext, "[薪资表]", String.format("公式解析 %s 失败", formula));
            }
        } else {
            if (!SalaryAdjustFormula.FORMULA0.equals(salaryAdjustFormula)) {
                jobRepository.warning(simpleStepContext, "[薪资表]", "调薪调整基数未设置");
            }
        }
        if (salaryAdjustFormula==null || SalaryAdjustFormula.FORMULA0.equals(salaryAdjustFormula)){
            salaryCalcContext.salaryAdjustPhasedCalcEnable = false;
            jobRepository.info(simpleStepContext, "[薪资表]", "分段调薪调整未开启");
        }else{
            salaryCalcContext.salaryAdjustPhasedCalcEnable = true;
            jobRepository.info(simpleStepContext, "[薪资表]", "分段调薪调整已开启");
        }

        List<SalaryField> salaryFields = simplePayrollConfig.getFields();

        ValidationUtils.rejectIfEmpty(salaryFields, null, "配置表项目为空");

        Map<SalaryFieldType, List<SalaryField>> salaryFieldTypeMap = salaryFields.stream()
                .filter(s -> s.getType() == null || !SalaryFieldType.Code.statistics.equals(s.getType().getCode()))
                .collect(Collectors.groupingBy(s -> s.getType()));

        Map<String, SalaryField> staticsFieldMap = salaryFields.stream().filter(s -> s.getType() != null && SalaryFieldType.Code.statistics.equals(s.getType().getCode())).collect(Collectors.toMap(s -> s.getCode(), s -> s));

        for (Map.Entry<SalaryFieldType, List<SalaryField>> entry : salaryFieldTypeMap.entrySet()) {
            SalaryFieldType type = entry.getKey();
            List<SalaryField> sfs = entry.getValue();
            if (type.isGenTypeTotalField() && CollectionUtils.isNotEmpty(sfs)) {
                //生成分类统计项目
                String code = type.totalCode();
                SalaryField stasticsField = staticsFieldMap.get(code);

                //字段不为空的字段已经在service层进行了处理。
                if (stasticsField != null) {
                    continue;
                }
                if (stasticsField == null) {
                    //生成临时字段
                    SystemSalaryField systemSalaryField = salaryCalcContext.getSystemSalaryField(code);
                    stasticsField = new SalaryField();
                    stasticsField.setCode(code);
                    if (systemSalaryField != null) {
                        stasticsField.setName(systemSalaryField.getName());
                        SalaryFieldValue fieldValue = new SalaryFieldValue();
                        SystemSalaryFieldValue systemSalaryFieldValue = systemSalaryField.getValueMeta();
                        fieldValue.setPattern(systemSalaryFieldValue.getPattern());
                        fieldValue.setRoundingMode(systemSalaryFieldValue.getRoundingMode());
                        stasticsField.setValueMeta(fieldValue);
                    } else {
                        SalaryFieldValue fieldValue = new SalaryFieldValue(2, RoundingMode.ROUND);
                        stasticsField.setName(type.getName().concat("合计"));
                        stasticsField.setValueMeta(fieldValue);
                    }
                    staticsFieldMap.put(code, stasticsField);
                }
                SalaryFieldValue valueMeta = stasticsField.getValueMeta();
                if (FormulaFrom.USR.equals(valueMeta.getFormulaFrom())) {
                    jobRepository.info(simpleStepContext, "[薪资表]", String.format("用户自定义公式 %s%s", stasticsField.getCode(), valueMeta.getFormula()));
                    continue;
                }
                StringBuilder builder = new StringBuilder();
                StringBuilder namebuilder = new StringBuilder();
                builder.append(ExprToken.EQUAL.val);
                namebuilder.append(ExprToken.EQUAL.val);
                for (int index = 0; index < sfs.size(); index++) {
                    SalaryField field = sfs.get(index);
                    if (index == 0) {
                        builder.append(field.getCode());
                        namebuilder.append(field.getName());
                    } else {
                        builder.append(ExprToken.PLUS.val);
                        builder.append(field.getCode());
                        namebuilder.append(ExprToken.PLUS.val);
                        namebuilder.append(field.getName());
                    }
                }
                String formula = builder.toString();
                String nameFormula = namebuilder.toString();
                if (!formula.equals(valueMeta.getFormula())) {
                    valueMeta.setFormula(formula);
                    valueMeta.setFormulaFrom(FormulaFrom.CALC);
                    stasticsField.setValueMeta(valueMeta);
                    jobRepository.trace(simpleStepContext, "[薪资表]", String.format("动态生成公式 %s%s", stasticsField.getCode(), formula));
                    jobRepository.trace(simpleStepContext, "[薪资表]", String.format("动态生成公式 %s%s", stasticsField.getName(), nameFormula));
                    staticsFieldMap.put(code, stasticsField);
                }
            }
        }

        Map<String, SalaryField> salaryFieldMap = salaryFields.stream().collect(Collectors.toMap(s -> s.getCode(), s -> s));

        for (Map.Entry<String, SalaryField> entry : staticsFieldMap.entrySet()) {
            String key = entry.getKey();
            SalaryField salaryField = entry.getValue();
            salaryFieldMap.put(key, salaryField);
        }
        return salaryFieldMap;
    }

    @Autowired
    public void setJobRepository(JobRepository jobRepository) {
        this.jobRepository = jobRepository;
    }


    @BeforeProcess
    public void setSimpleStepContext(SimplePayrollConfig items, SimpleStepContext simpleStepContext) {
        this.simpleStepContext = simpleStepContext;
    }
}